package com.kj.tms.common.utils.calx.util;

import com.kj.tms.common.utils.StringUtil;
import org.apache.commons.lang3.StringUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @ProjectName: ledger
 * @Package: com.hongtu.tms.ledger.calx.factory
 * @ClassName: Lexer
 * @Description: [词法解析器]
 * @Author: [zhuyongjie]
 * @CreateDate: 2018/4/11 18:02
 * @UpdateUser: []
 * @UpdateDate: 2018/4/11 18:02
 * @UpdateRemark: []
 * @Version: 1.0
 * Copyright: 北京宏图天安 Copyright (c) 2018
 */
public class Lexer {
    private final Integer EOF = 1;
    //当前解析公式文本
    private String _input;
    private boolean _more = false,
            _backtrack = false,
            done = false;//解析完成标识
    private String yytext = "",//单元格编码
            matched = "",//已匹配项
            match = "";//当前匹配项
    private Integer yylineno = 0,//单元格所在行
            yyleng = 0;//单元格编码长度
    private final String[] conditionStack = {"INITIAL"};
    private Map<String, Integer> yylloc;
    private Integer offset = 0;
    private Map<String, Object> yy = new HashMap<String, Object>();
    //随机数
    private String ranges;
    private List<String> rules;


    public Lexer() {
        initRulesData();
    }

    /**
     * @Method      initRulesData
     * @Param
     * @Return      void
     * @Exception
     * @Description [初始化匹配规则]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/4/20 16:28
     */
    private void initRulesData() {
        this.rules = new ArrayList<String>();
        Rules rules = new Rules();
        this.rules = rules.getRules();
    }

    public void setInput(String input) {
        this.set_input(input);
        yylloc = new HashMap<String, Integer>();
        yylloc.put("first_line", 1);
        yylloc.put("first_column", 0);
        yylloc.put("last_line", 1);
        yylloc.put("last_column", 0);
        this._more = false;
        this._backtrack = false;
        this.done = false;
        this.yylineno = this.yyleng = 0;
        this.yytext = "";
        this.match = "";
        this.matched = "";
        this.offset = 0;
    }//setInput end

    public String get_input() {
        return _input;
    }

    public void set_input(String _input) {
        this._input = _input;
    }

    public Map<String, Object> getYy() {
        return yy;
    }

    public void setYy(Map<String, Object> yy) {
        this.yy = yy;
    }

    public Map<String, Integer> getYylloc() {
        return yylloc;
    }

    public void setYylloc(Map<String, Integer> yylloc) {
        this.yylloc = yylloc;
    }

    public String getRanges() {
        return ranges;
    }

    public void setRanges(String ranges) {
        this.ranges = ranges;
    }

    /**
     * @Method lex
     * @Param
     * @Return java.lang.Object
     * @Exception
     * @Description [词法分析]
     * @Author zhuyongjie
     * @Version 1.0
     * @Date 2018/4/12 10:02
     */
    public Object lex() throws Exception {
        Object r = this.next();
        if (!StringUtil.isEmptyOrNull(r)) {
            return r;
        } else {
            return lex();
        }
    }//lex() end

    /**
     * @Method next
     * @Param
     * @Return java.lang.Object
     * @Exception
     * @Description [逐步分析]
     * @Author zhuyongjie
     * @Version 1.0
     * @Date 2018/4/12 10:13
     */
    private Object next() throws Exception {
        if (this.done) {
            //处理结束
            return this.EOF;
        }
        if (StringUtil.isEmptyOrNull(this._input)) {
            //公式为空时解析结束
            this.done = true;
        }

        Object token;
        //标识当前匹配项位置
        int index = 0;
        List<String> tempMatch = new ArrayList<String>(),
                match = new ArrayList<String>();
        if (!this._more) {
            this.yytext = "";
            this.match = "";
        }

        //循环查找匹配语法项
        for (int i = 0; i < this.rules.size(); i++) {
            String rule = this.rules.get(i);
//            tempMatch = Utility.execJavascriptMatch(rule, this.get_input());
            tempMatch = Utility.match(rule,this.get_input(),false);
            if ((!StringUtil.isEmptyOrNull(tempMatch)
                    && tempMatch.size() > 0)
                    && (!StringUtil.isEmptyOrNull(match)
                    || tempMatch.get(0).length() > match.get(0).length())) {
                //找到匹配项
//                System.out.println(tempMatch.toString() + '\t' + i);
                match.addAll(tempMatch);
                index = i;
                //todo:回滚
                break;//结束匹配
            }//if end
        }//for end

        if (!StringUtil.isEmptyOrNull(match)) {
            //获取token
            token = test_match(match, index);
            if (!StringUtil.isEmptyOrNull(token)) {
                return token;
            } else {
                return null;
            }//if end
        }//if end

        if (StringUtil.isEmptyOrNull(this._input)) {
            return this.EOF;
        } else {
            throw new Exception("Lexical error on line "
                    + (this.yylineno + 1) + ". Unrecognized text.\n" + this.showPosition());
        }//if end
        //todo:
    }//next() end

    /**
     * @Method test_match
     * @Param match
     * @Param indexed_rule
     * @Return java.lang.Object
     * @Exception
     * @Description [词法分析获取token]
     * @Author zhuyongjie
     * @Version 1.0
     * @Date 2018/4/13 10:10
     */
    private Object test_match(List<String> match, int indexed_rule) {
        Object token;
        List<String> lines;
        //todo:备份

        String str = StringUtil.toString(match.get(0));
//        lines = Utility.execJavascriptMatch("/(?:\\r\\n?|\\n).*/g", str);
        lines = Utility.match("(?:\\r\\n?|\\n).*", str,false);
        if (!StringUtil.isEmptyOrNull(lines) && lines.size() > 0) {
            this.yylineno += lines.size();
        }
        Integer last_line = this.yylloc.get("last_line");
        Integer last_column = this.yylloc.get("last_column");
        this.yylloc.put("first_line", last_line);
        this.yylloc.put("last_line", this.yylineno + 1);
        this.yylloc.put("first_column", last_column);
        int last = (!StringUtil.isEmptyOrNull(lines) && lines.size() > 0)
                ? lines.get(lines.size() - 1).length()
                        //- Utility.execJavascriptMatch("/\\r?\\n?/", lines.get(lines.size() - 1)).get(0)
                - Utility.match("\\r?\\n?", lines.get(lines.size() - 1),false).get(0)
                .length()
                : last_column + str.length();
        this.yylloc.put("last_column", last);

        this.yytext += str;
        this.match += str;
        this.yyleng = this.yytext.length();

        this._more = false;
        this._backtrack = false;
        this.set_input(this.get_input().substring(str.length()));
        this.matched += str;

        token = this.performAction(indexed_rule);
        if(this.done && !StringUtil.isEmptyOrNull(this.get_input())){
            this.done = false;
        }

        if(!StringUtil.isEmptyOrNull(token)){
            return  token;
        }else if(this._backtrack){
            //
            return null;
        }
        return null;
    }//test_match end

    /**
     * @Method      performAction
     * @Param		indexed_rule
     * @Return      java.lang.Object
     * @Exception
     * @Description [获取token]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/4/13 10:53
     */
    private Object performAction(int indexed_rule) {
        switch (indexed_rule){
            case 0:
                break;
            case 1:
                return 13;
            case 2:
                return 13;
            case 3:
                return 35;
            case 4:
                return 30;
            case 5:
                return 10;
            case 6:
                return 11;
            case 7:
                return 32;
            case 8:
                return 34;
            case 9:
                return 37;
            case 10:
                return 30;
            case 11:
                return 40;
            case 12:
                return 40;
            case 13:
                return 42;
            case 14:
                        /* skip whitespace */
                break;
            case 15:
                return ' ';
            case 16:
                return 41;

            case 17:
                return 33;

            case 18:
                return 38;

            case 19:
                return 39;

            case 20:
                return 26;

            case 21:
                return 27;

            case 22:
                return 25;

            case 23:
                return 16;

            case 24:
                return 28;

            case 25:
                return 17;

            case 26:
                return 18;

            case 27:
                return 19;

            case 28:
                return 21;

            case 29:
                return 23;

            case 30:
                return 22;

            case 31:
                return 24;

            case 32:
                return "PI";

            case 33:
                return 29;

            case 34:
                return 7;

            case 35:
                return 8;

            case 36:
                return 9;

            case 37:
                return '"';

            case 38:
                return "'";

            case 39:
                return "!";

            case 40:
                return 15;

            case 41:
                return 43;

            case 42:
                return 44;

            case 43:
                return 14;

            case 44:
                return 5;
        }
        return null;
    }// performAction end

    /**
     * @Method showPosition
     * @Param
     * @Return java.lang.String
     * @Exception
     * @Description [解析公式位置]
     * @Author zhuyongjie
     * @Version 1.0
     * @Date 2018/4/13 9:55
     */
    public String showPosition() {
        String pre = this.pastInput();
        String c = StringUtils.join(new String[pre.length() + 1], "-");
        return pre + this.upcomingInput() + "\n" + c + "^";
    }// showPosition end

    private String upcomingInput() {
        String next = this.match;
//        if (next.length() < 20) {
            next += this.get_input();
//        }
       /* return ((next.length() > 20 ?(next.substring(0, 20) + "...")  : "")).replaceAll("\n", "");*/
       return next.replaceAll("\n","");
    }//upcomingInput end

    private String pastInput() {
        String past = this.matched.substring(0, this.matched.length() - this.match.length());

        return (past.length() > 20 ? "..." : "") + past.replaceAll("\n", "");
    }//pastInput end



    public String getYytext() {
        return yytext;
    }

    public void setYytext(String yytext) {
        this.yytext = yytext;
    }

    public Integer getYylineno() {
        return yylineno;
    }

    public void setYylineno(Integer yylineno) {
        this.yylineno = yylineno;
    }

    public Integer getYyleng() {
        return yyleng;
    }

    public void setYyleng(Integer yyleng) {
        this.yyleng = yyleng;
    }

    public static void main(String[] args) {
        Lexer lexer = new Lexer();
//        lexer.setInput("  fsaf ");//开头空格
//        lexer.setInput("\"SUM(A1:A2)\"");//开头双引号
//        lexer.setInput("'SUM(A1:A2)'");//开头单引号
//        lexer.setInput("#A1_rwqr");//开头井号文本
//        lexer.setInput("IF(A1>A2,A2)");
//        lexer.setInput("if(a2>a1,a2)");
//        lexer.setInput("sum1");
//        lexer.setInput("5:53:32pm");//时间格式
//        lexer.setInput("17:53:24");
//        lexer.setInput("$A$6");
//        lexer.setInput("B3");
//        lexer.setInput("C:A");
//        lexer.setInput("2:3");
//        lexer.setInput("SUM(A1:A2)");
//        lexer.setInput("AMT_2");
//        lexer.setInput("A_A_");
//        lexer.setInput("42342");
//        lexer.setInput("$A1");
//        lexer.setInput(".fsad");
//        lexer.setInput(":A2");
//        lexer.setInput(";A");
//        lexer.setInput(",A");
//        lexer.setInput("*");
//        lexer.setInput("_fsad");
//        lexer.setInput("/");
//        lexer.setInput("-");
//        lexer.setInput("+");
//        lexer.setInput("^fsaf");
//        lexer.setInput("(");
//        lexer.setInput(")");
//        lexer.setInput("[");
//        lexer.setInput("]");
//        lexer.setInput(">");
//        lexer.setInput("<");
//        lexer.setInput("NOT");
//        lexer.setInput("fdsafPI");
//        lexer.setInput("E+off");
//        lexer.setInput("!sheet");
//        lexer.setInput("=A1+a2");
//        lexer.setInput("%f");
//        lexer.setInput("#");
//        lexer.setInput("&");
//        lexer.setInput("");


        lexer.setInput("SUMIF(AMTA_4:AMTB_4,>10,AMTA_4:AMTB_4)");


        try {
            //词法分析
            lexer.lex();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }


}
